// WinMacroDialog2.cpp : implementation file

// This file contains code for the UI
// The dialog itself, action handlers.etc

#include <afxwin.h>
#include <afxdlgs.h>
#include <afxtempl.h>
#include <sstream>
#include "hooks.h"

#include "resource.h"
#include "WinMacroDialog2.h"
#include "AboutDialog.h"
#include "OptionsDialog.h"
#include "StatusWindow.h"

#include "../WinMacroLib/WinMacroLib.h"
#include "WinMacroCommandLine.h"

extern HINSTANCE hinstance;

BOOL playing_back;
WinMacroDialog2 * dialog_ptr;

BOOL recording;

const UINT_PTR TIMER_ID = 1234;

#define WM_STOP_RECORDING (WM_USER + 100)
#define WM_STOP_PLAYBACK (WM_USER + 101)

#define START_RECORD 1
#define START_PLAYBACK 2

void CALLBACK HandleRecordingStopped()
{
	dialog_ptr->PostMessage(WM_STOP_RECORDING);
}

void CALLBACK HandlePlaybackStopped()
{	
	dialog_ptr->PostMessage(WM_STOP_PLAYBACK);
}

void CALLBACK HandlePlaybackProgress(PLAYBACKPROGRESSINFO progress_info)
{
	dialog_ptr->ReportPlaybackProgress(progress_info);
}


/////////////////////////////////////////////////////////////////////////////
// WinMacroDialog2 dialog


WinMacroDialog2::WinMacroDialog2(CWnd* pParent /*=NULL*/)
: CDialog(WinMacroDialog2::IDD, pParent), status(Idle), action_start_time(0), m_pStatusWindow(NULL)
{
	//Setup bool variable to indicate that when start recording is pressed, it indicates
	//system is recording.
	recording=FALSE;

	dialog_ptr=this;
}


void WinMacroDialog2::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(WinMacroDialog2)
	DDX_Control(pDX, IDC_OPTIONS, m_options_button);
	DDX_Control(pDX, IDC_BROWSE, m_browse_button);
	DDX_Control(pDX, IDC_FILENAME, m_filename);
	DDX_Control(pDX, IDC_PLAYBACK, m_playback_button);
	DDX_Control(pDX, IDC_RECORD, m_record_button);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(WinMacroDialog2, CDialog)
	//{{AFX_MSG_MAP(WinMacroDialog2)
	ON_BN_CLICKED(IDC_RECORD, OnRecord_Click)
	ON_BN_CLICKED(IDC_PLAYBACK, OnPlayback_Click)
	ON_BN_CLICKED(IDC_BROWSE, OnBrowse_Click)
	ON_BN_CLICKED(IDC_ABOUT, OnAbout_Click)
	ON_BN_CLICKED(IDC_OPTIONS, OnOptions_Click)
	ON_MESSAGE(WM_STOP_PLAYBACK, OnStopPlayback)
	ON_MESSAGE(WM_STOP_RECORDING, OnStopRecording)
	ON_WM_HOTKEY()
	ON_WM_CLOSE()
	//}}AFX_MSG_MAP

END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// WinMacroDialog2 message handlers

BOOL WinMacroDialog2::OnInitDialog() 
{
	//Perform basic initialization in this function

	CDialog::OnInitDialog();

	//Load application icon
	HICON icon= LoadIcon(hinstance,MAKEINTRESOURCE(IDI_ICON1));
	SetIcon(icon,TRUE);

	//Create Statusbar with only one panel
	statusbar.Create(WS_CHILD|WS_VISIBLE|CCS_BOTTOM,CRect(0,0,100,10),this,123);
	statusbar.SetSimple(FALSE);   
	int widtharray[1]={400};
	statusbar.SetParts(1,widtharray);

	statusbar.SetText("Ready to record",0,0);
	OptionsPersister persister;
	RECORDINGOPTIONS recording_options; PLAYBACKOPTIONS playback_options; UIOPTIONS ui_options;
	persister.ReadOptions(recording_options, playback_options, ui_options);

	if( m_initialPath.GetLength() > 0) {

		WinMacroCommandLine command_line((LPCTSTR)m_initialPath);

		command_line.ParseAndUpdateOptions(recording_options, playback_options);
		const std::string &file_name = command_line.GetFileName();
		m_filename.SetWindowText(file_name.c_str());

		if (command_line.GetAction() == Playback)
		{
			StartPlayback(file_name, playback_options);
		}
		else if (command_line.GetAction() == Record)
		{
			StartRecording(file_name, recording_options);
		}
		return TRUE;
	}

	
	m_filename.SetWindowText(ui_options.most_recent_macro_file.c_str());

	RegisterHotkeys();

	return TRUE;  // return TRUE unless you set the focus to a control
}



void WinMacroDialog2::OnRecord_Click() 
{
	StartRecordingWithCurrentSettings();
}


void WinMacroDialog2::StartRecordingWithCurrentSettings()
{
	CString record_filename;

	//This prevents recording to start again, when this button is clicked during playback either
	//accidentally or intentionally. If this were not here, recording would begin again at 
	//the end of playback, automatically. 
	//This is because the click on the button is also recorded to the file, even if it's on 
	//the disabled button. During playback, the "Record" button is enabled, 
	//so the click will register and WinMacro would start recording again.
	if (playing_back) return;

	//Get pointer to button, so that we can enable/disable the Start Recording button

	//If file to record is okay
	OptionsPersister persister;
	RECORDINGOPTIONS recording_options; PLAYBACKOPTIONS playback_options; UIOPTIONS ui_options;
	persister.ReadOptions(recording_options, playback_options, ui_options);

	m_filename.GetWindowText(record_filename);
	StartRecording((LPCTSTR)record_filename, recording_options);
}

void WinMacroDialog2::StartRecording(const std::string &filename, const RECORDINGOPTIONS &recording_options)
{
	if (CheckFileName(filename, "Please enter a valid filename to record to."))
	{
		CWnd *wnd = GetForegroundWindow();

		DWORD error = RecordTo((LPCTSTR)filename.c_str(), recording_options, HandleRecordingStopped);

		if (error)
		{
			ShowError(error);
		}
		else
		{
			status = Recording;
			action_start_time = GetTickCount();
			EnableDisableUI(FALSE);

			ShowWindow(SW_MINIMIZE);
			CWnd *pNextWindow = GetForegroundWindow();
			ShowStatusWindow();
			::SetForegroundWindow(wnd->m_hWnd);
		}
	}
}

void WinMacroDialog2::ShowFileDialog (CEdit *textbox)
{
	/**************************
	Utility function that encapsulates showing the open file dialog box
	and filling the supplied CEdit with the selected filename
	***************************/

	CFileDialog file_dialog(TRUE,"winmacro.log");

	//If user has not pressed cancel
	if (file_dialog.DoModal()==IDOK)
	{
		textbox->SetWindowText(file_dialog.GetPathName());
	}
}

BOOL WinMacroDialog2::CheckFileName(const std::string &filename, const std::string &error_mesg, bool must_exist)
{
	/*************************
	Utility function that checks whether filename_field contains a valid filename.
	If so, tries opening stream in mode passed as parameter.
	If any of these fail, the error_mesg supplied is displayed and the function
	returns false
	*************************/
	if (filename.empty())
	{
		MessageBox(error_mesg.c_str(), "WinMacro Error");
		return FALSE;
	}

	if (must_exist)
	{
		std::ifstream in(filename.c_str());

		if (!in)
		{
			std::string combined_error_message = error_mesg + "File does not exist.";
			MessageBox(combined_error_message.c_str(), "WinMacro Error");
			return FALSE;
		}
	}

	return TRUE;
}

void WinMacroDialog2::OnPlayback_Click() 
{
	StartPlaybackWithCurrentSettings();
}

void WinMacroDialog2::StartPlaybackWithCurrentSettings()
{	
	//Set flag variable to indicate we are playing back.  This is needed because a click to the "Stop Recording" button 
	//of this application is also stored when recording. When the file is played back, that button gets clicked again, but this 
	//time "Start Recording" gets clicked,(because it's playing back, not recording). This unfortunately causes recording to begin
	//again. Took lots of time to figure this out :)
	//playing_back=TRUE;
	CString playback_filename;
	m_filename.GetWindowText(playback_filename);

	OptionsPersister persister;
	RECORDINGOPTIONS recording_options; PLAYBACKOPTIONS playback_options; UIOPTIONS ui_options;
	persister.ReadOptions(recording_options, playback_options, ui_options);

	StartPlayback((LPCTSTR)playback_filename, playback_options);
}

void WinMacroDialog2::StartPlayback(const std::string &filename, const PLAYBACKOPTIONS &playback_options)
{
	CWnd *wnd = GetForegroundWindow();

	if (CheckFileName(filename, "Please enter a valid filename to playback.", true))
	{
		DWORD error = PlaybackFrom(filename.c_str(), playback_options, HandlePlaybackStopped, HandlePlaybackProgress);

		if (error)
		{
			ShowError(error);
		}
		else
		{
			status = PlayingBack;
			action_start_time = GetTickCount();

			EnableDisableUI(FALSE);
			ShowWindow(SW_MINIMIZE);
			ShowStatusWindow();
			::SetForegroundWindow(wnd->m_hWnd);
		}
	}
}

LRESULT WinMacroDialog2::OnStopPlayback(WPARAM, LPARAM)
{
	StopPlayback();
	return TRUE;
}

LRESULT WinMacroDialog2::OnStopRecording(WPARAM, LPARAM)
{
	StopRecording();
	return TRUE;
}

void WinMacroDialog2::StopRecording()
{	
	HideStatusWindow();
	EnableDisableUI(true);
	SetFocus();

	status = Idle;
	//Change status to finished recording
	statusbar.SetText("Finished recording",0,0);

	ShowWindow(SW_RESTORE);
}

void WinMacroDialog2::StopPlayback()
{	
	HideStatusWindow();

	EnableDisableUI(true);
	SetFocus();

	status = Idle;

	//Change status to finished recording
	statusbar.SetText("Finished playback",0,0);

	OptionsPersister persister;
	RECORDINGOPTIONS recording_options; PLAYBACKOPTIONS playback_options; UIOPTIONS ui_options;
	persister.ReadOptions(recording_options, playback_options, ui_options);

	if (ui_options.shutdown_after_playback)
	{
		this->PostMessageA(WM_CLOSE);
	}

	ShowWindow(SW_RESTORE);
}

void WinMacroDialog2::ReportPlaybackProgress(PLAYBACKPROGRESSINFO progress_info)
{
	this->playback_progress_info = progress_info;

	// ShowStatusWindow is called only after successfully installing the playback hook, so
	// it is possible that the ReportProgressCallback could get called before the window is created.
	if (m_pStatusWindow)
		m_pStatusWindow->UpdateUI(); // Safe to call because this callback comes on the UI thread.
}

void WinMacroDialog2::OnBrowse_Click() 
{
	ShowFileDialog(&m_filename);	
}

void WinMacroDialog2::OnAbout_Click() 
{
	//Show the about dialog. The simplest of all :)
	AboutDialog about_dialog;
	about_dialog.DoModal();

}

void WinMacroDialog2::OnOptions_Click() 
{
	OptionsDialog dialog;

	if (dialog.DoModal() == IDOK)
	{
		UnregisterHotKey(this->m_hWnd, START_RECORD);
		UnregisterHotKey(this->m_hWnd, START_PLAYBACK);

		RegisterHotkeys();
	}
}

void WinMacroDialog2::EnableDisableUI(BOOL p)
{
	m_record_button.EnableWindow(p);
	m_filename.EnableWindow(p);
	m_browse_button.EnableWindow(p);
	m_playback_button.EnableWindow(p);
	m_options_button.EnableWindow(p);
}

void WinMacroDialog2::InitializeOptions()
{
}

void WinMacroDialog2::ShowStatusWindow()
{
	m_pStatusWindow = new StatusWindow();
	m_pStatusWindow->Create(IDD_STATUS_DIALOG, this);
	m_pStatusWindow->ShowWindow(SW_SHOWNOACTIVATE);
}

void WinMacroDialog2::HideStatusWindow()
{
	m_pStatusWindow->DestroyWindow();
	delete m_pStatusWindow;
	m_pStatusWindow = NULL;
}

void WinMacroDialog2::OnHotKey(UINT nHotKeyId, UINT modifiers, UINT key)
{
	if (status != Idle)
		return;

	switch(nHotKeyId)
	{
		case START_RECORD : StartRecordingWithCurrentSettings(); break;
		case START_PLAYBACK : StartPlaybackWithCurrentSettings(); break;
	}
}

void WinMacroDialog2::SaveLastMacroFileName()
{
	OptionsPersister persister;
	RECORDINGOPTIONS recording_options; PLAYBACKOPTIONS playback_options; UIOPTIONS ui_options;
	persister.ReadOptions(recording_options, playback_options, ui_options);

	CString filename;
	m_filename.GetWindowText(filename);

	ui_options.most_recent_macro_file = std::string(filename); 
	persister.WriteOptions(recording_options, playback_options, ui_options);
}

void WinMacroDialog2::RegisterHotkeys()
{
	OptionsPersister persister;
	RECORDINGOPTIONS recording_options; PLAYBACKOPTIONS playback_options; UIOPTIONS ui_options;
	persister.ReadOptions(recording_options, playback_options, ui_options);

	RegisterHotKey(this->m_hWnd, START_RECORD, ui_options.recording_hotkey_modifier, ui_options.recording_hotkey_virtualkey);
	RegisterHotKey(this->m_hWnd, START_PLAYBACK, ui_options.playback_hotkey_modifier, ui_options.playback_hotkey_virtualkey);
}

void WinMacroDialog2::ShowError(DWORD error)
{
	LPVOID buffer = NULL;
	FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER , NULL, error, 0, (LPSTR) &buffer, 512, NULL);
	MessageBox((LPCTSTR)buffer, "WinMacro Error");
	LocalFree((HLOCAL)buffer);
}

void WinMacroDialog2::OnClose()
{
	SaveLastMacroFileName();
	CDialog::OnClose();
}